// Copyright (C) 2011 nitsuja and contributors
// Copyright (C) 2013 Hourglass-Resurrection team
// Hourglass-Resurrection is licensed under GPL v3.
// Full notice is in COPYING.txt.

#if !defined(GDIHOOKS_INCL) && !defined(UNITY_BUILD)
#define GDIHOOKS_INCL


#include "../wintasee.h"
#include "../tls.h"
#include "../../shared/winutil.h"
#include "../gwi.h"
#include "../../shared/ipc.h"
#include <windowsx.h>
#include <wchar.h>

#include "../phasedetection.h"
//static PhaseDetector s_gdiPhaseDetector;

#include "../locale.h"

// Fix for Windows XP/Vista, these are not supported by the OS until Win7, but we don't care, because we don't support them either
// We just need certain functions to not break on XP/Vista
#if(WINVER < 0x0600)
#define CDS_ENABLE_UNSAFE_MODES      0x00000100
#define CDS_DISABLE_UNSAFE_MODES     0x00000200
#endif

// TODO: declare these in headers like openglhooks.h?
// I don't like having lots of almost-empty header files, though.
bool PresentOGLD3D();
void FakeBroadcastDisplayChange(int width, int height, int depth);

// TODO: Rewrite AVI dumping for GDI.
// TODO: Re-order many hooked functions so that the structure makes more sense

static void FrameBoundaryDIBitsToAVI(const void* bits, const BITMAPINFO& bmi)
{
	DDSURFACEDESC desc = { sizeof(DDSURFACEDESC) };
	desc.lpSurface = const_cast<LPVOID>(bits);
	desc.dwWidth = bmi.bmiHeader.biWidth;
	desc.dwHeight = bmi.bmiHeader.biHeight;
	desc.lPitch = bmi.bmiHeader.biWidth * (bmi.bmiHeader.biBitCount >> 3);
	// correct for upside-down bitmaps.
	if((LONG)desc.dwHeight < 0)
	{
		// FillFrame won't understand negative height.
		desc.dwHeight = -(LONG)desc.dwHeight;
	}

	else
	{
		// give WriteAVIFrame negative pitch.
		(char*&)desc.lpSurface += (desc.dwHeight - 1) * desc.lPitch;
		desc.lPitch = -desc.lPitch;
	}
	desc.ddpfPixelFormat.dwRGBBitCount = bmi.bmiHeader.biBitCount;

	bool valid = true;
	switch(bmi.bmiHeader.biCompression)
	{
	case BI_RGB:
		switch(bmi.bmiHeader.biBitCount)
		{
		case 32:
		case 24:
			desc.ddpfPixelFormat.dwRBitMask = 0xFF0000;
			desc.ddpfPixelFormat.dwGBitMask = 0x00FF00;
			desc.ddpfPixelFormat.dwBBitMask = 0x0000FF;
			break;
		case 16: // RGB555
			desc.ddpfPixelFormat.dwRBitMask = 0x7C00;
			desc.ddpfPixelFormat.dwGBitMask = 0x03E0;
			desc.ddpfPixelFormat.dwBBitMask = 0x001F;
			break;
		case 8: {
			int numEntries = 256;
			if(bmi.bmiHeader.biClrUsed)
				numEntries = min(256, bmi.bmiHeader.biClrUsed);
			for(int i = 0; i < numEntries; i++)
			{
				gwi.palPalette[i].peBlue = bmi.bmiColors[i].rgbBlue;
				gwi.palPalette[i].peGreen = bmi.bmiColors[i].rgbGreen;
				gwi.palPalette[i].peRed = bmi.bmiColors[i].rgbRed;
				gwi.palPalette[i].peFlags = bmi.bmiColors[i].rgbReserved;
			}
		}	break;
		default:
			valid = false;
			break;
		}
		break;
	case BI_BITFIELDS:
		// probably RGB565
		desc.ddpfPixelFormat.dwRBitMask = 0[(DWORD*)bmi.bmiColors];
		desc.ddpfPixelFormat.dwGBitMask = 1[(DWORD*)bmi.bmiColors];
		desc.ddpfPixelFormat.dwBBitMask = 2[(DWORD*)bmi.bmiColors];
		break;
	default:
		valid = false;
		break;
	}

	if(valid)
	{
		FrameBoundary(&desc, CAPTUREINFO_TYPE_DDSD);
	}
	else
	{
		debuglog(LCF_GDI|LCF_ERROR, __FUNCTION__": unhandled bit count %d with biCompression=%d\n", bmi.bmiHeader.biBitCount, bmi.bmiHeader.biCompression);
		FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE);
	}
}

struct FULLBITMAPINFO
{
	BITMAPINFO bmi;
    RGBQUAD remainingColors [255];
};

//#define UNSELECT_BEFORE_HDC_CAPTURE // TODO: if this doesn't slow down AVI capture of GDI games and it doesn't break anything when savestates are loaded during AVI capture of GDI games, then enable it

static void FrameBoundaryHDCtoAVI(HDC hdc,int xSrc,int ySrc,int xRes,int yRes)
{
#ifdef UNSELECT_BEFORE_HDC_CAPTURE
	// the docs say: "The bitmap identified by the hbmp parameter must not be selected into a device context when the application calls this function."
	HBITMAP bitmap = (HBITMAP)SelectObject(hdc, CreateCompatibleBitmap(hdc, 1, 1));
#else
	// but this appears to work fine and it's probably at least a little bit faster, so...
	HBITMAP bitmap = (HBITMAP)GetCurrentObject(hdc, OBJ_BITMAP);
#endif

	FULLBITMAPINFO fbmi = {sizeof(BITMAPINFOHEADER)};
	BITMAPINFO& bmi = *(BITMAPINFO*)&fbmi;
	GetDIBits(hdc, bitmap, 0, 0, 0, &bmi, DIB_RGB_COLORS);

	static char* bits = NULL;
	static unsigned int bitsAllocated = 0;
	if(bitsAllocated < bmi.bmiHeader.biSizeImage)
	{
		bits = (char*)realloc(bits, bmi.bmiHeader.biSizeImage);
		bitsAllocated = bmi.bmiHeader.biSizeImage;
	}

	int height = bmi.bmiHeader.biHeight;
	if(height < 0) height = -height;
		
	GetDIBits(hdc, bitmap, 0, height, bits, &bmi, DIB_RGB_COLORS);
	FrameBoundaryDIBitsToAVI(bits, bmi);

#ifdef UNSELECT_BEFORE_HDC_CAPTURE
	DeleteObject(SelectObject(hdc, bitmap));
#endif
}

static const int gdiFrameBigEnoughWidth = 120;
static const int gdiFrameBigEnoughHeight = 80;
static bool HDCSizeBigEnoughForFrameBoundary(HDC hdc)
{
#ifdef UNSELECT_BEFORE_HDC_CAPTURE
	// the docs say: "The bitmap identified by the hbmp parameter must not be selected into a device context when the application calls this function."
	HBITMAP bitmap = (HBITMAP)SelectObject(hdc, CreateCompatibleBitmap(hdc, 1, 1));
#else
	// but this appears to work fine and it's probably at least a little bit faster, so...
	HBITMAP bitmap = (HBITMAP)GetCurrentObject(hdc, OBJ_BITMAP);
#endif

	BITMAPINFO bmi = {sizeof(BITMAPINFOHEADER)};
	GetDIBits(hdc, bitmap, 0, 0, 0, &bmi, DIB_RGB_COLORS);

	int width = bmi.bmiHeader.biWidth;
	int height = bmi.bmiHeader.biHeight;
	if(height < 0) height = -height;

	bool bigEnough = (width >= gdiFrameBigEnoughWidth && height >= gdiFrameBigEnoughHeight);

#ifdef UNSELECT_BEFORE_HDC_CAPTURE
	DeleteObject(SelectObject(hdc, bitmap));
#endif

	return bigEnough;
}



int depth_SwapBuffers = 0;
HOOKFUNC BOOL MySwapBuffers(HDC hdc)
{
	depth_SwapBuffers++;
	//if(!usingSDLOrDD)
	if(!redrawingScreen)
	{
//		int localFramecount = framecount;
//		if(localFramecount == framecount)
		if(!PresentOGLD3D())
		{
			SwapBuffers(hdc);
		// TODO: FrameBoundaryHDCtoAVI here?
			FrameBoundary();
		}
	}
	//else
	{
// maybe this branch is just broken?
// rescue: the beagles crashes with no clear callstack if we get here.
// disabled for now.
		//SwapBuffers(hdc);
	}
	depth_SwapBuffers--;

	return TRUE;
}




void RescaleRect(RECT& rect, RECT from, RECT to)
{
	rect.left   = ((rect.left   - from.left) * (to.right - to.left)) / (from.right - from.left) + to.left;
	rect.top    = ((rect.top    - from.top)  * (to.bottom - to.top)) / (from.bottom - from.top) + to.top;
	rect.right  = ((rect.right  - from.left) * (to.right - to.left)) / (from.right - from.left) + to.left;
	rect.bottom = ((rect.bottom - from.top)  * (to.bottom - to.top)) / (from.bottom - from.top) + to.top;
}





static HDC s_hdcSrcSaved;
static HDC s_hdcDstSaved;
static bool s_gdiPendingRefresh;
bool RedrawScreenGDI();


HOOKFUNC BOOL WINAPI MyStretchBlt(
	HDC hdcDest,
	int nXOriginDest, int nYOriginDest,
	int nWidthDest, int nHeightDest,
	HDC hdcSrc,
	int nXOriginSrc, int nYOriginSrc,
	int nWidthSrc, int nHeightSrc,
	DWORD dwRop)
{
	bool isFrameBoundary = false;
	if(!usingSDLOrDD /*&& !inPauseHandler*/ && !redrawingScreen)
	{
		if(dwRop == SRCCOPY)
		{
			HWND hwnd = WindowFromDC(hdcDest);
			if(hwnd /*&& !hwndRespondingToPaintMessage[hwnd]*/)
			{
				if((/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(nXOriginDest,nYOriginDest))
					||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted))
				{
					if((nWidthSrc >= gdiFrameBigEnoughWidth && nHeightSrc >= gdiFrameBigEnoughHeight)
					|| HDCSizeBigEnoughForFrameBoundary(hdcSrc))
					{
						isFrameBoundary = true;
					}
				}
			}
		}
	}

	debuglog(LCF_GDI|(isFrameBoundary?LCF_FRAME:LCF_FREQUENT), __FUNCTION__ " called.\n");


	BOOL rv;
	if(!ShouldSkipDrawing(false, WindowFromDC(hdcDest) != 0))
	{
		if(s_gdiPendingRefresh && !redrawingScreen)
		{
			redrawingScreen = true;
			RedrawScreenGDI();
			redrawingScreen = false;
		}
		if(OBJ_DC == GetObjectType(hdcDest) && (gwi.fullScreenGame || WindowFromDC(hdcDest) == NULL))
		{
			// According to DxWnd the "Silent Hunter II" intro movie (rendered by QuickTime 5) renders the video on the PrimarySurface->GetDC device
			// context, that is a memory device type associated to NULL (desktop) window, through GDI StretchBlt api. So, you shoud compensate
			// by scaling and offsetting to main window.
			gwi.MapToWindow(&nXOriginDest, &nYOriginDest, &nWidthDest, &nHeightDest);
		}
		rv = StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, dwRop);
	}
	else
		s_gdiPendingRefresh = true;

	if(isFrameBoundary)
	{
		tls.peekedMessage = FALSE;
		if(!(tasflags.aviMode & 1))
			FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE);
		else
			FrameBoundaryHDCtoAVI(hdcSrc,nXOriginSrc,nYOriginSrc,nWidthSrc,nHeightSrc);
		s_hdcSrcSaved = hdcSrc;
		s_hdcDstSaved = hdcDest;
	}

	return rv;
}
HOOKFUNC BOOL WINAPI MyBitBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, DWORD dwRop)
{
	bool isFrameBoundary = false;
	if(!usingSDLOrDD /*&& !inPauseHandler*/ && !redrawingScreen)
	{
		if(dwRop == SRCCOPY)
		{
			HWND hwnd = WindowFromDC(hdcDest);
			if(hwnd /*&& !hwndRespondingToPaintMessage[hwnd]*/)
			{
				if((/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(nXDest,nYDest))
					||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted))
				{
					if((nWidth >= gdiFrameBigEnoughWidth && nHeight >= gdiFrameBigEnoughHeight)
					|| HDCSizeBigEnoughForFrameBoundary(hdcSrc))
					{
						isFrameBoundary = true;
					}
				}
			}
		}
	}

	debuglog(LCF_GDI|(isFrameBoundary?LCF_FRAME:LCF_FREQUENT), __FUNCTION__ "(0x%X,%d,%d,%d,%d,0x%X,%d,%d,0x%X) called.\n", hdcDest,nXDest,nYDest,nWidth,nHeight,hdcSrc,nXSrc,nYSrc,dwRop);



	BOOL rv;
	if(!ShouldSkipDrawing(false, WindowFromDC(hdcDest) != 0))
	{
		if(s_gdiPendingRefresh && !redrawingScreen)
		{
			redrawingScreen = true;
			RedrawScreenGDI();
			redrawingScreen = false;
		}

		// TODO: Could this even be simplified to BitBlt?
		if((GetObjectType(hdcDest) == OBJ_DC && gwi.fullScreenGame) || WindowFromDC(hdcDest) == NULL)
		{
			int nWDest = nWidth;
			int nHDest = nHeight;
			gwi.MapToWindow(&nXDest, &nYDest, &nWDest, &nHDest);
			rv = StretchBlt(hdcDest, nXDest, nYDest, nWDest, nHDest, hdcSrc, nXSrc, nYSrc, nWidth, nHeight, dwRop);
		}
		else
			rv = BitBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, dwRop);

		//if(!fakeDisplayValid)
		//{
		//	rv = BitBlt(hdcDest,nXDest,nYDest,nWidth,nHeight,hdcSrc,nXSrc,nYSrc,dwRop);
		//}
		//else
		//{
			//HWND hwnd = WindowFromDC(hdcDest);
			//RECT realRect;
			//if(!GetClientRect(hwnd, &realRect) || (realRect.right == gwi.dwScreenWidth && realRect.bottom == gwi.dwScreenHeight))
			//{
			//	rv = BitBlt(hdcDest,nXDest,nYDest,nWidth,nHeight,hdcSrc,nXSrc,nYSrc,dwRop);
			//}
			//else
			//{
			//	// support resized fake-fullscreen windows in games like Lyle in Cube Sector
			//	// a little iffy: sprites leave pixels behind occasionally at non-integral scales
			//	HDC hdcTemp = 0;
			//	HDC hdc = hdcDest;
			//	if(realRect.right > gwi.dwScreenWidth || realRect.bottom > gwi.dwScreenHeight)
			//	{
			//		// sidestep clip region (it can't be expanded without switching HDCs)
			//		hdcTemp = GetDC(hwnd);
			//		hdc = hdcTemp;
			//	}
			//	// FIXME this feature actually broke, it's drawing at 100% size again no matter the window size,
			//	// it probably broke because of extra hooking of functions like GetClientRect.
			//	RECT dstRect = {nXDest, nYDest, nXDest+nWidth, nYDest+nHeight};
			//	RECT fakeRect = {0, 0, gwi.dwScreenWidth, gwi.dwScreenHeight};
			//	RescaleRect(dstRect, fakeRect, realRect);
			//	rv = StretchBlt(hdc, dstRect.left,dstRect.top,dstRect.right-dstRect.left,dstRect.bottom-dstRect.top, hdcSrc, nXSrc,nYSrc,nWidth,nHeight, dwRop);
			//	if(hdcTemp)
			//		ReleaseDC(hwnd, hdcTemp);
			//}
		//}
	}
	else
		s_gdiPendingRefresh = true;

	if(isFrameBoundary)
	{
		tls.peekedMessage = FALSE;
		if(!(tasflags.aviMode & 1))
			FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE);
		else
			FrameBoundaryHDCtoAVI(hdcSrc,nXSrc,nYSrc,nWidth,nHeight);
		s_hdcSrcSaved = hdcSrc;
		s_hdcDstSaved = hdcDest;
	}

	return rv;
}

HOOKFUNC BOOL WINAPI MyPatBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, DWORD dwRop)
{
	bool isFrameBoundary = false;
	if(!usingSDLOrDD /*&& !inPauseHandler*/ && !redrawingScreen)
	{
		//if(dwRop == SRCCOPY)
		//{
			HWND hwnd = WindowFromDC(hdcDest);
			if(hwnd /*&& !hwndRespondingToPaintMessage[hwnd]*/)
			{
				if((/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(nXDest,nYDest))
					||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted))
				{
					if((nWidth >= gdiFrameBigEnoughWidth && nHeight >= gdiFrameBigEnoughHeight)
					|| HDCSizeBigEnoughForFrameBoundary(hdcDest)) // hdcDest is all we got
					{
						isFrameBoundary = true;
					}
				}
			}
		//}
	}

	debuglog(LCF_GDI|(isFrameBoundary?LCF_FRAME:LCF_FREQUENT), __FUNCTION__ "(0x%X,%d,%d,%d,%d,0x%X) called.\n", hdcDest,nXDest,nYDest,nWidth,nHeight,dwRop);

	BOOL rv;

	if(!ShouldSkipDrawing(false, WindowFromDC(hdcDest) != 0))
	{
		if(s_gdiPendingRefresh && !redrawingScreen)
		{
			redrawingScreen = true;
			RedrawScreenGDI();
			redrawingScreen = false;
		}
	
		if(OBJ_DC == GetObjectType(hdcDest) && (gwi.fullScreenGame || WindowFromDC(hdcDest) == NULL))
		{
			gwi.MapToWindow(&nXDest, &nYDest, &nWidth, &nHeight);
		}

		rv = PatBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, dwRop);
	}
	else
		s_gdiPendingRefresh = true;

	// TODO: Is this even remotely correct?
	if(isFrameBoundary)
	{
		tls.peekedMessage = FALSE;
		if(!(tasflags.aviMode & 1))
			FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE);
		else
			FrameBoundaryHDCtoAVI(hdcDest, nXDest, nYDest, nWidth, nHeight);
		//s_hdcSrcSaved = hdcSrc;
		s_hdcDstSaved = hdcDest;
	}

	return rv;
}

HOOKFUNC BOOL WINAPI MyMaskBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc,
					 int nXSrc, int nYSrc, HBITMAP hbmMask, int xMask, int yMask, DWORD dwRop)
{
		bool isFrameBoundary = false;
	if(!usingSDLOrDD /*&& !inPauseHandler*/ && !redrawingScreen)
	{
		if(dwRop == SRCCOPY)
		{
			HWND hwnd = WindowFromDC(hdcDest);
			if(hwnd /*&& !hwndRespondingToPaintMessage[hwnd]*/)
			{
				if((/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(nXDest,nYDest))
					||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted))
				{
					if((nWidth >= gdiFrameBigEnoughWidth && nHeight >= gdiFrameBigEnoughHeight)
					|| HDCSizeBigEnoughForFrameBoundary(hdcSrc))
					{
						isFrameBoundary = true;
					}
				}
			}
		}
	}

	// BitMask not included due to it's variable size...
	debuglog(LCF_GDI|(isFrameBoundary?LCF_FRAME:LCF_FREQUENT), __FUNCTION__ "(0x%X,%d,%d,%d,%d,0x%X,%d,%d,0x%X) called.\n", hdcDest,nXDest,nYDest,nWidth,nHeight,hdcSrc,nXSrc,nYSrc,dwRop);

	BOOL rv;

	if(!ShouldSkipDrawing(false, WindowFromDC(hdcDest) != 0))
	{
		if(s_gdiPendingRefresh && !redrawingScreen)
		{
			redrawingScreen = true;
			RedrawScreenGDI();
			redrawingScreen = false;
		}

		if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdcDest)))
		{
			gwi.MapToWindow(&nXDest, &nYDest, &nWidth, &nHeight);
		}

		rv = MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc, hbmMask, xMask, yMask, dwRop);
	}
	else
		s_gdiPendingRefresh = true;

	if(isFrameBoundary)
	{
		tls.peekedMessage = FALSE;
		if(!(tasflags.aviMode & 1))
			FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE);
		else
			FrameBoundaryHDCtoAVI(hdcSrc,nXSrc,nYSrc,nWidth,nHeight);
		s_hdcSrcSaved = hdcSrc;
		s_hdcDstSaved = hdcDest;
	}

	return rv;
}

bool RedrawScreenGDI()
{
	s_gdiPendingRefresh = false;
	if(!s_hdcSrcSaved || usingSDLOrDD)
		return false;
	RECT rect;
	if(!GetClientRect(WindowFromDC(s_hdcDstSaved), &rect))
		return false;
	BITMAP srcBitmap;
	if(!GetObject((HBITMAP)GetCurrentObject(s_hdcSrcSaved,OBJ_BITMAP), sizeof(BITMAP), &srcBitmap))
		return false;
	MyStretchBlt(s_hdcDstSaved,0,0,rect.right,rect.bottom,s_hdcSrcSaved,0,0,srcBitmap.bmWidth,srcBitmap.bmHeight,SRCCOPY);
	return true;
}


HOOKFUNC int WINAPI MySetDIBitsToDevice(HDC hdc, int XDest, int YDest, DWORD dwWidth, DWORD dwHeight, int XSrc, int YSrc, UINT uStartScan, UINT cScanLines, const VOID * lpvBits, const BITMAPINFO * lpbmi, UINT fuColorUse)
{
	if(gwi.fullScreenGame && gwi.IsVirtual(hdc))
	{
		int X = XDest + gwi.VirtualOffsetX;
		int Y = YDest + gwi.VirtualOffsetY;

		int rv = SetDIBitsToDevice(hdc, X, Y, dwWidth, dwHeight, XSrc, YSrc, uStartScan, cScanLines, lpvBits, lpbmi, fuColorUse);
		if(!usingSDLOrDD /*&& !inPauseHandler*/ && !redrawingScreen)
		{
			HWND hWnd = WindowFromDC(hdc);
			if(gwi.IsDesktop(hWnd)) hWnd = gwi.hWnd;
			//if(hwnd /*&& !hwndRespondingToPaintMessage[hwnd]*/)
			//{
				if(rv != 0 && rv != GDI_ERROR && (/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(xDest,yDest))
					||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted))
				{
					if(!(tasflags.aviMode & 1))
						FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE);
					else
						FrameBoundaryDIBitsToAVI(lpvBits,*lpbmi);
					tls.peekedMessage = FALSE;
				}
			//}
		}
		return rv;
	}
	
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		// TODO: Update this comment to explain in better detail.

		// tricky part: CreateCompatibleBitmap is needed to set the dc size, but it has to be performed
		// against hdc to set for color depth, then selected (through SelectObject) against the temporary
		// dc to assign the needed size and color space to the temporary dc.

		gwi.MapToWindow(&XDest, &YDest);

		HDC hTempDc = CreateCompatibleDC(hdc);
		HBITMAP hbmPic = CreateCompatibleBitmap(hdc, dwWidth, dwHeight);
		SelectObject(hTempDc, hbmPic);
		SetDIBitsToDevice(hTempDc, 0, 0, dwWidth, dwHeight, XSrc, YSrc, uStartScan, cScanLines, lpvBits, lpbmi, fuColorUse);
		MyBitBlt(hdc, XDest, YDest, dwWidth, dwHeight, hTempDc, 0, 0, SRCCOPY); // Let our BitBlt deal with AVI stuff etc.
		DeleteObject(hbmPic);
		DeleteDC(hTempDc);
		return cScanLines;
	}
	else
	{
		return SetDIBitsToDevice(hdc, XDest, YDest, dwWidth, dwHeight, XSrc, YSrc, uStartScan, cScanLines, lpvBits, lpbmi, fuColorUse);
	}
	//int rv = SetDIBitsToDevice(hdc, XDest, YDest, dwWidth, dwHeight, XSrc, YSrc, uStartScan, cScanLines, lpvBits, lpbmi, fuColorUse);
	//if(!usingSDLOrDD /*&& !inPauseHandler*/ && !redrawingScreen)
	//{
	//	HWND hwnd = WindowFromDC(hdc);
	//	if(hwnd /*&& !hwndRespondingToPaintMessage[hwnd]*/)
	//	{
	//		if(rv != 0 && rv != GDI_ERROR && (/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(xDest,yDest))
	//			||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted))
	//		{
	//			if(!(tasflags.aviMode & 1))
	//				FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE);
	//			else
	//				FrameBoundaryDIBitsToAVI(lpvBits,*lpbmi);
	//			tls.peekedMessage = FALSE;
	//		}
	//	}
	//}
	//return rv;
}

HOOKFUNC int WINAPI MyStretchDIBits(HDC hdc, int XDest, int YDest, int nDestWidth, int nDestHeight, int XSrc, int YSrc, int nSrcWidth, int nSrcHeight, const VOID *lpBits, const BITMAPINFO *lpbmi, UINT iUsage, DWORD dwRop)
{
	if(gwi.fullScreenGame)
	{
		if(gwi.IsVirtual(hdc))
		{
			XDest += gwi.VirtualOffsetX;
			YDest += gwi.VirtualOffsetY;
		}
		else if(OBJ_DC == GetObjectType(hdc))
		{
			gwi.MapToWindow(&XDest, &YDest, &nDestWidth, &nDestHeight);
		}
	}

	int rv = StretchDIBits(hdc, XDest, YDest, nDestWidth, nDestHeight, XSrc, YSrc, nSrcWidth, nSrcHeight, lpBits, lpbmi, iUsage, dwRop);
	if(!usingSDLOrDD /*&& !inPauseHandler*/ && !redrawingScreen)
	{
		HWND hWnd = WindowFromDC(hdc);
		if(gwi.IsDesktop(hWnd)) hWnd = gwi.hWnd;
		//if(hwnd /*&& !hwndRespondingToPaintMessage[hwnd]*/)
		//{
			if(rv != 0 && rv != GDI_ERROR && dwRop == SRCCOPY && (/*s_gdiPhaseDetector.AdvanceAndCheckCycleBoundary(MAKELONG(xDest,yDest))
				||*/ tls.peekedMessage) && VerifyIsTrustedCaller(!tls.callerisuntrusted))
			{
				if(!(tasflags.aviMode & 1))
					FrameBoundary(NULL, CAPTUREINFO_TYPE_NONE);
				else
					FrameBoundaryDIBitsToAVI(lpBits,*lpbmi);
				tls.peekedMessage = FALSE;
			}
		//}
	}
	return rv;
}

HOOKFUNC HDC WINAPI MyCreateDCA(LPCSTR lpszDriver, LPCSTR lpszDevice, LPCSTR lpszOutput, const DEVMODEA *lpInitData)
{
	HDC rv;

	// Screen DC requested
	if (lpszDriver == NULL || strncmp(lpszDriver, "DISPLAY", 7) != 0)
	{
		HDC hWindowDC = GetDC(gwi.hWnd);
		rv = CreateCompatibleDC(hWindowDC);
		ReleaseDC(gwi.hWnd, hWindowDC);
	}
	else
	{
		rv = CreateDCA(lpszDriver, lpszDevice, lpszOutput, lpInitData);
	}
	
	return rv;
}
HOOKFUNC HDC WINAPI MyCreateDCW(LPWSTR lpszDriver, LPWSTR lpszDevice, LPWSTR lpszOutput, const DEVMODEW *lpInitData)
{
	HDC rv;

	// Screen DC requested
	if (lpszDriver == NULL || wcsncmp(lpszDriver, L"DISPLAY", 7) != 0)
	{
		HDC hWindowDC = GetDC(gwi.hWnd);
		rv = CreateCompatibleDC(hWindowDC);
		ReleaseDC(gwi.hWnd, hWindowDC);
	}
	else
	{
		rv = CreateDCW(lpszDriver, lpszDevice, lpszOutput, lpInitData);
	}
	
	return rv;
}

// GDI version of the call, do not confuse with the DirectDraw verison.
HOOKFUNC HDC WINAPI MyGetDC(HWND hWnd)
{
	HDC rv;
	HWND lochwnd;

	lochwnd = hWnd;
	if (gwi.IsRealDesktop(hWnd))
	{
		lochwnd = gwi.hWnd;
	}

	if(gwi.bEmulateDC)
		rv = gwi.AcquireEmulatedDC(lochwnd);
	else
		rv = GetDC(lochwnd);
	
	if(!rv)
	{
		int err;
		err = GetLastError();
		if((err == ERROR_INVALID_WINDOW_HANDLE) && (lochwnd != hWnd))
		{
			rv = GetDC(hWnd);	
		}
	}

	return rv;
}

// TODO: Handle more of it?
// Used by Star Wars Shadow of the Empire according to DxWnd.
HOOKFUNC HDC WINAPI MyGetDCEx(HWND hWnd, HRGN hrgnClip, DWORD flags)
{
	HDC rv;
	HWND lochwnd;

	lochwnd = hWnd;
	if (gwi.IsRealDesktop(hWnd))
	{
		lochwnd = gwi.hWnd;
	}

	if(gwi.bEmulateDC)
		rv = gwi.AcquireEmulatedDC(lochwnd);
	else
		rv = GetDC(lochwnd);
	
	if(!rv)
	{
		int err;
		err = GetLastError();
		if((err == ERROR_INVALID_WINDOW_HANDLE) && (lochwnd != hWnd))
		{
			rv = GetDCEx(hWnd, hrgnClip, flags);
		}
	}

	return rv;
}

// TODO: Include in the emulated DC, it will be possible if the emulated DC is of the Ex type.
HOOKFUNC HDC WINAPI MyGetWindowDC(HWND hWnd)
{
	HDC rv;
	HWND lochwnd = hWnd;
	
	if(gwi.IsRealDesktop(hWnd))
	{
		lochwnd = gwi.hWnd;
	}

	rv = GetWindowDC(lochwnd);

	if(!rv)
	{
		int err = GetLastError();
		if((err == ERROR_INVALID_WINDOW_HANDLE) && (lochwnd != hWnd))
		{
			rv = GetWindowDC(hWnd);
		}
	}
	return rv;
}

HOOKFUNC int WINAPI MyReleaseDC(HWND hWnd, HDC hDC)
{
	if (gwi.IsRealDesktop(hWnd)) hWnd = gwi.hWnd;

	if(gwi.bEmulateDC)
		return gwi.ReleaseEmulatedDC(hWnd);
	else
		return ReleaseDC(hWnd, hDC);
}

HPALETTE hFakeDesktopPalette = NULL;

HOOKFUNC HPALETTE WINAPI MySelectPalette(HDC hdc, HPALETTE hpal, BOOL bForceBackground)
{
	HPALETTE rv = SelectPalette(hdc, hpal, bForceBackground);
	if(GetObjectType(hdc) == OBJ_DC)
	{
		hFakeDesktopPalette = hpal;
	}
	return rv;
}

HOOKFUNC UINT WINAPI MyRealizePalette(HDC hdc)
{
	if(OBJ_DC == GetObjectType(hdc))
	{
		PALETTEENTRY PalEntries[256];
		UINT nEntries;
		nEntries = GetPaletteEntries(hFakeDesktopPalette, 0, 256, PalEntries);
		gwi.SetPalette(0, nEntries, PalEntries);
		
		return DD_OK;
	}
	else
		return RealizePalette(hdc);
}

// Default 8bit-depth palette taken from DxWnd
// In some cases games fail to get the system palette when the games are in 8BPP mode,
// therefor such a palette needs to be provided so that we can fix that.
// According to DxWnd "Mission Force Cyberstorm" is one of these games.
static PALETTEENTRY pal8BitPalette[256] = {
	{0x00,0x00,0x00,0x00},{0x80,0x00,0x00,0x00},{0x00,0x80,0x00,0x00},{0x80,0x80,0x00,0x00},
	{0x00,0x00,0x80,0x00},{0x80,0x00,0x80,0x00},{0x00,0x80,0x80,0x00},{0xc0,0xc0,0xc0,0x00},
	{0xa0,0xa0,0xa0,0x00},{0xf0,0xf0,0xf0,0x00},{0xc0,0xdc,0xc0,0x00},{0xa6,0xca,0xf0,0x00},
	{0x04,0x04,0x04,0x00},{0x08,0x08,0x08,0x00},{0x0c,0x0c,0x0c,0x00},{0x11,0x11,0x11,0x00},
	{0x16,0x16,0x16,0x00},{0x1c,0x1c,0x1c,0x00},{0x22,0x22,0x22,0x00},{0x29,0x29,0x29,0x00},
	{0x55,0x55,0x55,0x00},{0x4d,0x4d,0x4d,0x00},{0x42,0x42,0x42,0x00},{0x39,0x39,0x39,0x00},
	{0xff,0x7c,0x80,0x00},{0xff,0x50,0x50,0x00},{0xd6,0x00,0x93,0x00},{0xcc,0xec,0xff,0x00},
	{0xef,0xd6,0xc6,0x00},{0xe7,0xe7,0xd6,0x00},{0xad,0xa9,0x90,0x00},{0x33,0x00,0x00,0x00},
	{0x66,0x00,0x00,0x00},{0x99,0x00,0x00,0x00},{0xcc,0x00,0x00,0x00},{0x00,0x33,0x00,0x00},
	{0x33,0x33,0x00,0x00},{0x66,0x33,0x00,0x00},{0x99,0x33,0x00,0x00},{0xcc,0x33,0x00,0x00},
	{0xff,0x33,0x00,0x00},{0x00,0x66,0x00,0x00},{0x33,0x66,0x00,0x00},{0x66,0x66,0x00,0x00},
	{0x99,0x66,0x00,0x00},{0xcc,0x66,0x00,0x00},{0xff,0x66,0x00,0x00},{0x00,0x99,0x00,0x00},
	{0x33,0x99,0x00,0x00},{0x66,0x99,0x00,0x00},{0x99,0x99,0x00,0x00},{0xcc,0x99,0x00,0x00},
	{0xff,0x99,0x00,0x00},{0x00,0xcc,0x00,0x00},{0x33,0xcc,0x00,0x00},{0x66,0xcc,0x00,0x00},
	{0x99,0xcc,0x00,0x00},{0xcc,0xcc,0x00,0x00},{0xff,0xcc,0x00,0x00},{0x66,0xff,0x00,0x00},
	{0x99,0xff,0x00,0x00},{0xcc,0xff,0x00,0x00},{0x00,0x00,0x33,0x00},{0x33,0x00,0x33,0x00},
	{0x66,0x00,0x33,0x00},{0x99,0x00,0x33,0x00},{0xcc,0x00,0x33,0x00},{0xff,0x00,0x33,0x00},
	{0x00,0x33,0x33,0x00},{0x33,0x33,0x33,0x00},{0x66,0x33,0x33,0x00},{0x99,0x33,0x33,0x00},
	{0xcc,0x33,0x33,0x00},{0xff,0x33,0x33,0x00},{0x00,0x66,0x33,0x00},{0x33,0x66,0x33,0x00},
	{0x66,0x66,0x33,0x00},{0x99,0x66,0x33,0x00},{0xcc,0x66,0x33,0x00},{0xff,0x66,0x33,0x00},
	{0x00,0x99,0x33,0x00},{0x33,0x99,0x33,0x00},{0x66,0x99,0x33,0x00},{0x99,0x99,0x33,0x00},
	{0xcc,0x99,0x33,0x00},{0xff,0x99,0x33,0x00},{0x00,0xcc,0x33,0x00},{0x33,0xcc,0x33,0x00},
	{0x66,0xcc,0x33,0x00},{0x99,0xcc,0x33,0x00},{0xcc,0xcc,0x33,0x00},{0xff,0xcc,0x33,0x00},
	{0x33,0xff,0x33,0x00},{0x66,0xff,0x33,0x00},{0x99,0xff,0x33,0x00},{0xcc,0xff,0x33,0x00},
	{0xff,0xff,0x33,0x00},{0x00,0x00,0x66,0x00},{0x33,0x00,0x66,0x00},{0x66,0x00,0x66,0x00},
	{0x99,0x00,0x66,0x00},{0xcc,0x00,0x66,0x00},{0xff,0x00,0x66,0x00},{0x00,0x33,0x66,0x00},
	{0x33,0x33,0x66,0x00},{0x66,0x33,0x66,0x00},{0x99,0x33,0x66,0x00},{0xcc,0x33,0x66,0x00},
	{0xff,0x33,0x66,0x00},{0x00,0x66,0x66,0x00},{0x33,0x66,0x66,0x00},{0x66,0x66,0x66,0x00},
	{0x99,0x66,0x66,0x00},{0xcc,0x66,0x66,0x00},{0x00,0x99,0x66,0x00},{0x33,0x99,0x66,0x00},
	{0x66,0x99,0x66,0x00},{0x99,0x99,0x66,0x00},{0xcc,0x99,0x66,0x00},{0xff,0x99,0x66,0x00},
	{0x00,0xcc,0x66,0x00},{0x33,0xcc,0x66,0x00},{0x99,0xcc,0x66,0x00},{0xcc,0xcc,0x66,0x00},
	{0xff,0xcc,0x66,0x00},{0x00,0xff,0x66,0x00},{0x33,0xff,0x66,0x00},{0x99,0xff,0x66,0x00},
	{0xcc,0xff,0x66,0x00},{0xff,0x00,0xcc,0x00},{0xcc,0x00,0xff,0x00},{0x00,0x99,0x99,0x00},
	{0x99,0x33,0x99,0x00},{0x99,0x00,0x99,0x00},{0xcc,0x00,0x99,0x00},{0x00,0x00,0x99,0x00},
	{0x33,0x33,0x99,0x00},{0x66,0x00,0x99,0x00},{0xcc,0x33,0x99,0x00},{0xff,0x00,0x99,0x00},
	{0x00,0x66,0x99,0x00},{0x33,0x66,0x99,0x00},{0x66,0x33,0x99,0x00},{0x99,0x66,0x99,0x00},
	{0xcc,0x66,0x99,0x00},{0xff,0x33,0x99,0x00},{0x33,0x99,0x99,0x00},{0x66,0x99,0x99,0x00},
	{0x99,0x99,0x99,0x00},{0xcc,0x99,0x99,0x00},{0xff,0x99,0x99,0x00},{0x00,0xcc,0x99,0x00},
	{0x33,0xcc,0x99,0x00},{0x66,0xcc,0x66,0x00},{0x99,0xcc,0x99,0x00},{0xcc,0xcc,0x99,0x00},
	{0xff,0xcc,0x99,0x00},{0x00,0xff,0x99,0x00},{0x33,0xff,0x99,0x00},{0x66,0xcc,0x99,0x00},
	{0x99,0xff,0x99,0x00},{0xcc,0xff,0x99,0x00},{0xff,0xff,0x99,0x00},{0x00,0x00,0xcc,0x00},
	{0x33,0x00,0x99,0x00},{0x66,0x00,0xcc,0x00},{0x99,0x00,0xcc,0x00},{0xcc,0x00,0xcc,0x00},
	{0x00,0x33,0x99,0x00},{0x33,0x33,0xcc,0x00},{0x66,0x33,0xcc,0x00},{0x99,0x33,0xcc,0x00},
	{0xcc,0x33,0xcc,0x00},{0xff,0x33,0xcc,0x00},{0x00,0x66,0xcc,0x00},{0x33,0x66,0xcc,0x00},
	{0x66,0x66,0x99,0x00},{0x99,0x66,0xcc,0x00},{0xcc,0x66,0xcc,0x00},{0xff,0x66,0x99,0x00},
	{0x00,0x99,0xcc,0x00},{0x33,0x99,0xcc,0x00},{0x66,0x99,0xcc,0x00},{0x99,0x99,0xcc,0x00},
	{0xcc,0x99,0xcc,0x00},{0xff,0x99,0xcc,0x00},{0x00,0xcc,0xcc,0x00},{0x33,0xcc,0xcc,0x00},
	{0x66,0xcc,0xcc,0x00},{0x99,0xcc,0xcc,0x00},{0xcc,0xcc,0xcc,0x00},{0xff,0xcc,0xcc,0x00},
	{0x00,0xff,0xcc,0x00},{0x33,0xff,0xcc,0x00},{0x66,0xff,0x99,0x00},{0x99,0xff,0xcc,0x00},
	{0xcc,0xff,0xcc,0x00},{0xff,0xff,0xcc,0x00},{0x33,0x00,0xcc,0x00},{0x66,0x00,0xff,0x00},
	{0x99,0x00,0xff,0x00},{0x00,0x33,0xcc,0x00},{0x33,0x33,0xff,0x00},{0x66,0x33,0xff,0x00},
	{0x99,0x33,0xff,0x00},{0xcc,0x33,0xff,0x00},{0xff,0x33,0xff,0x00},{0x00,0x66,0xff,0x00},
	{0x33,0x66,0xff,0x00},{0x66,0x66,0xcc,0x00},{0x99,0x66,0xff,0x00},{0xcc,0x66,0xff,0x00},
	{0xff,0x66,0xcc,0x00},{0x00,0x99,0xff,0x00},{0x33,0x99,0xff,0x00},{0x66,0x99,0xff,0x00},
	{0x99,0x99,0xff,0x00},{0xcc,0x99,0xff,0x00},{0xff,0x99,0xff,0x00},{0x00,0xcc,0xff,0x00},
	{0x33,0xcc,0xff,0x00},{0x66,0xcc,0xff,0x00},{0x99,0xcc,0xff,0x00},{0xcc,0xcc,0xff,0x00},
	{0xff,0xcc,0xff,0x00},{0x33,0xff,0xff,0x00},{0x66,0xff,0xcc,0x00},{0x99,0xff,0xff,0x00},
	{0xcc,0xff,0xff,0x00},{0xff,0x66,0x66,0x00},{0x66,0xff,0x66,0x00},{0xff,0xff,0x66,0x00},
	{0x66,0x66,0xff,0x00},{0xff,0x66,0xff,0x00},{0x66,0xff,0xff,0x00},{0xa5,0x00,0x21,0x00},
	{0x5f,0x5f,0x5f,0x00},{0x77,0x77,0x77,0x00},{0x86,0x86,0x86,0x00},{0x96,0x96,0x96,0x00},
	{0xcb,0xcb,0xcb,0x00},{0xb2,0xb2,0xb2,0x00},{0xd7,0xd7,0xd7,0x00},{0xdd,0xdd,0xdd,0x00},
	{0xe3,0xe3,0xe3,0x00},{0xea,0xea,0xea,0x00},{0xff,0xfb,0xf0,0x00},{0xa0,0xa0,0xa4,0x00},
	{0x80,0x80,0x80,0x00},{0xff,0x00,0x00,0x00},{0x00,0xff,0x00,0x00},{0xff,0xff,0x00,0x00},
	{0x00,0x00,0xff,0x00},{0xff,0x00,0xff,0x00},{0x00,0xff,0xff,0x00},{0xff,0xff,0xff,0x00}
};

HOOKFUNC UINT WINAPI MyGetSystemPaletteEntries(HDC hdc, UINT iStartIndex, UINT nEntries, LPPALETTEENTRY lppe)
{
	UINT rv = GetSystemPaletteEntries(hdc, iStartIndex, nEntries, lppe);
	if(rv == 0) // TODO: Enable only in compat mode?
	{
		// Getting the system palette failed, which means we tried to get an 8bit one.
		for(UINT i = 0; i < nEntries; i++)
			lppe[i] = pal8BitPalette[iStartIndex+i]; 
		return nEntries;
	}
	return rv;
}

// TODO: DrawDib support needed?




HOOKFUNC int WINAPI MyChoosePixelFormat(HDC hdc, CONST PIXELFORMATDESCRIPTOR* pfd)
{
	debuglog(LCF_GDI, __FUNCTION__ " called.\n");
	int rv = ChoosePixelFormat(hdc,pfd);
	return rv;
}
int depth_SetPixelFormat = 0;
HOOKFUNC BOOL WINAPI MySetPixelFormat(HDC hdc, int format, CONST PIXELFORMATDESCRIPTOR * pfd)
{
	debuglog(LCF_UNTESTED|LCF_GDI, __FUNCTION__ " called.\n");
	BOOL rv;
	//if(!tasflags.forceWindowed)
	//{
	//	depth_SetPixelFormat++;
	//	rv = SetPixelFormat(hdc,format,pfd);
	//	depth_SetPixelFormat--;
	//}
	//else
		rv = TRUE;
	return rv;
}
HOOKFUNC COLORREF WINAPI MyGetPixel(HDC hdc, int xx, int yy)
{
	debuglog(LCF_TODO|LCF_UNTESTED|LCF_DESYNC|LCF_GDI, __FUNCTION__ " called.\n");
	COLORREF rv = GetPixel(hdc,xx,yy);
	//debugprintf("XXX: 0x%08X", rv);
	return rv;
}

HOOKFUNC int WINAPI MyGetDeviceCaps(HDC hdc, int index)
{
//	debuglog(LCF_TODO|LCF_UNTESTED|LCF_DESYNC|LCF_GDI, __FUNCTION__ "(%d) called.\n", index);
	int rv;
	
	switch(index)
	{
		case HORZRES:
		case DESKTOPHORZRES:
			rv = gwi.dwScreenWidth;
			break;
		case VERTRES:
		case DESKTOPVERTRES:
			rv = gwi.dwScreenHeight;
			break;
		case BITSPIXEL:
			rv = gwi.dwPixelFormatBPP;
			break;
		case VREFRESH:
			rv = gwi.dwDisplayRefresh;
			break;
		case TECHNOLOGY:
			rv = DT_RASDISPLAY;
			break;
		case PLANES:
			rv = 1; 
			break;
		case LOGPIXELSX:
		case LOGPIXELSY:
			rv = 96;
			break;
		// TODO cases for compat modes, currently fall through to default.
		//case BITSPIXEL: // TODO: Add user option in compat to fake 8 BPP and 16 BPP
		case COLORRES:

		case RASTERCAPS: // TODO: Add flag RC_PALETTE on 8 BPP compat

		case SIZEPALETTE: // TOOD: Use only in 32bit exe / 16 bit driver compat mode.
		case NUMRESERVED:
		default:
			rv = GetDeviceCaps(hdc,index);
	}
	debuglog(/*(got?0:(LCF_TODO|LCF_UNTESTED|LCF_DESYNC))|*/LCF_GDI, __FUNCTION__ "(%d) called, returned %d (0x%X).\n", index, rv, rv);
	return rv;
}

HOOKFUNC HDC WINAPI MyBeginPaint(HWND hwnd, LPPAINTSTRUCT lpPaint)
{
	HDC hdc = BeginPaint(hwnd, lpPaint);

	if(!gwi.fullScreenGame) return hdc;

	if(gwi.bEmulateDC) return gwi.AcquireEmulatedDC(hwnd);

	return hdc;
}

HOOKFUNC BOOL WINAPI MyEndPaint(HWND hwnd, const PAINTSTRUCT *lpPaint)
{
	BOOL rv;
	
	if(gwi.bEmulateDC && gwi.fullScreenGame)
	{
		rv = gwi.ReleaseEmulatedDC(hwnd);
	}

	rv = EndPaint(hwnd, lpPaint);

	return rv;
}

HOOKFUNC int WINAPI MyFillRect(HDC hDC, const RECT *lprc, HBRUSH hbr)
{
	// Some games like "Premier Manager 98" sends excessively large RECTs according to DxWnd, so they need to be fixed.
	// ... And since this function insists on requiring a const RECT, we have to make a copy to play with,
	// we cannot simply assume that the game will pass us a non-const RECT and that we're creating the const-context here.
	RECT rc;
	memcpy(&rc, lprc, sizeof(RECT));

	if(rc.left < 0) rc.left = 0;
	if(rc.top < 0) rc.top = 0;
	if(rc.right > gwi.dwScreenWidth) rc.right = gwi.dwScreenWidth;
	if(rc.bottom > gwi.dwScreenHeight) rc.bottom = gwi.dwScreenHeight;

	// If the hDC is a device context so it will use device coordiantes to draw instead of logical, we also need to convert it to our window.
	// Think of it as screen vs client coordinates.
	if(GetObjectType(hDC) == OBJ_DC) gwi.MapToWindow(&rc);

	return FillRect(hDC, &rc, hbr);
}

HOOKFUNC int WINAPI MyFrameRect(HDC hDC, const RECT *lprc, HBRUSH hbr)
{
	// DxWnd treats this one differently from FillRect, when the same problem as with FillRect can very well exist here too.
	// Implementing it like FillRect
	// -- Warepire
	RECT rc;
	memcpy(&rc, lprc, sizeof(RECT));

	if(rc.left < 0) rc.left = 0;
	if(rc.top < 0) rc.top = 0;
	if(rc.right > gwi.dwScreenWidth) rc.right = gwi.dwScreenWidth;
	if(rc.bottom > gwi.dwScreenHeight) rc.bottom = gwi.dwScreenHeight;

	if(GetObjectType(hDC) == OBJ_DC) gwi.MapToWindow(&rc);

	return FrameRect(hDC, &rc, hbr);
}

HOOKFUNC BOOL WINAPI MyRectangle(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect)
{
	if(gwi.fullScreenGame && GetObjectType(hdc) == OBJ_DC)
	{
		gwi.MapToWindow(&nLeftRect, &nTopRect, &nRightRect, &nBottomRect);
	}
	return Rectangle(hdc, nLeftRect, nTopRect, nRightRect, nBottomRect);
}

HOOKFUNC BOOL WINAPI MyPolyline(HDC hdc, const POINT *lppt, int cPoints)
{
	// Adjust points to game window.
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		for(int i=0; i < cPoints; i++)
		{
			gwi.MapToWindow((LPPOINT)&lppt[i]);
		}
	}
	return Polyline(hdc, lppt, cPoints);
}

HOOKFUNC BOOL WINAPI MyLineTo(HDC hdc, int nXEnd, int nYEnd)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(&nXEnd, &nYEnd);
	}
	return LineTo(hdc, nXEnd, nYEnd);
}

HOOKFUNC BOOL WINAPI MyArcTo(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect, int nXRadial1, int nYRadial1, int nXRadial2, int nYRadial2)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(&nLeftRect, &nTopRect, &nRightRect, &nBottomRect);
		gwi.MapToWindow(&nXRadial1, &nYRadial1, &nXRadial2, &nYRadial2);
	}
	return ArcTo(hdc, nLeftRect, nTopRect, nRightRect, nBottomRect, nXRadial1, nYRadial1, nXRadial2, nYRadial2);
}

HOOKFUNC BOOL WINAPI MyMoveToEx(HDC hdc, int X, int Y, LPPOINT lpPoint)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(&X, &Y);
	}
	return MoveToEx(hdc, X, Y, lpPoint);
}

HOOKFUNC BOOL WINAPI MyPolyDraw(HDC hdc, const POINT *lppt, const BYTE *lpbTypes, int cCount)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		for(int i = 0; i < cCount; i++)
		{
			gwi.MapToWindow((LPPOINT)&lppt[i]);
		}
	}
	return PolyDraw(hdc, lppt, lpbTypes, cCount);
}

HOOKFUNC BOOL WINAPI MyPolylineTo(HDC hdc, const POINT *lppt, DWORD cCount)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		for(DWORD i = 0; i < cCount; i++)
		{
			gwi.MapToWindow((LPPOINT)&lppt[i]);
		}
	}
	return PolylineTo(hdc, lppt, cCount);
}

HOOKFUNC BOOL WINAPI MyPolyBezierTo(HDC hdc, const POINT *lppt, DWORD cCount)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		for(DWORD i = 0; i < cCount; i++)
		{
			gwi.MapToWindow((LPPOINT)&lppt[i]);
		}
	}
	return PolyBezierTo(hdc, lppt, cCount);
}

HOOKFUNC BOOL WINAPI MyEllipse(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(&nLeftRect, &nTopRect, &nRightRect, &nBottomRect);
	}
	return Ellipse(hdc, nLeftRect, nTopRect, nRightRect, nBottomRect);
}

HOOKFUNC BOOL WINAPI MyPolygon(HDC hdc, const POINT *lpPoints, int cCount)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		for(int i = 0; i < cCount; i++)
		{
			gwi.MapToWindow((LPPOINT)&lpPoints[i]);
		}
	}
	return Polygon(hdc, lpPoints, cCount);
}

HOOKFUNC BOOL WINAPI MyArc(HDC hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect, int nXStartArc, int nYStartArc, int nXEndArc, int nYEndArc)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(&nLeftRect, &nTopRect, &nRightRect, &nBottomRect);
		gwi.MapToWindow(&nXStartArc, &nYStartArc, &nXEndArc, &nYEndArc);
	}
	return Arc(hdc, nLeftRect, nTopRect, nRightRect, nBottomRect, nXStartArc, nYStartArc, nXEndArc, nYEndArc);
}

HOOKFUNC COLORREF WINAPI MySetPixel(HDC hdc, int X, int Y, COLORREF crColor)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(&X, &Y);
	}

	// TODO: Enable in Compat mode only. DxWnd doesn't do this, but there is a possibility of it being required...
	if(gwi.VirtualPixelFormat.dwRGBBitCount == 8)
	{
		crColor = gwi.GetMatchingColor(crColor);
	}

	return SetPixel(hdc, X, Y, crColor);
}

HOOKFUNC int WINAPI MyGetClipBox(HDC hdc, LPRECT lprc)
{
	// Needs to be hooked for Imperialism II (and probably other games) to prevent blit clipping.
	int rv;
	// Return codes: 0/"ERROR", 1/"NULLREGION", 2/"SIMPLEREGION", 3/"COMPLEXREGION"
	rv = GetClipBox(hdc, lprc);
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)) && (rv != 0))
	{
		// TODO: Add checks for overlapping windows and if they are game windows vs. other windows.
		// Also track clipping path / region changes and take those into account.

		// Currently just give the game the entire client rect of the window's DC.
		HWND hCurrentWindow = WindowFromDC(hdc);
		if(gwi.IsDesktop(hCurrentWindow)) hCurrentWindow = gwi.hWnd; // On NULL/Desktop hWnd, assume main window.
		GetClientRect(hCurrentWindow, lprc);
	}
	return rv;
}

HOOKFUNC HRGN WINAPI MyCreateEllipticRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect)
{
	if(gwi.fullScreenGame)
	{
		gwi.MapToWindow(&nLeftRect, &nTopRect, &nRightRect, &nBottomRect);
	}
	return CreateEllipticRgn(nLeftRect, nTopRect, nRightRect, nBottomRect);
}

HOOKFUNC HRGN WINAPI MyCreateEllipticRgnIndirect(const RECT *lprc)
{
	if(gwi.fullScreenGame)
	{
		gwi.MapToWindow((RECT *)lprc);
	}
	return CreateEllipticRgnIndirect(lprc);
}

HOOKFUNC HRGN WINAPI MyCreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect)
{
	if(gwi.fullScreenGame)
	{
		gwi.MapToWindow(&nLeftRect, &nTopRect, &nRightRect, &nBottomRect);
	}
	return CreateRectRgn(nLeftRect, nTopRect, nRightRect, nBottomRect);
}

HOOKFUNC HRGN WINAPI MyCreateRectRgnIndirect(const RECT *lprc)
{
	if(gwi.fullScreenGame)
	{
		gwi.MapToWindow((RECT *)lprc);
	}
	return CreateRectRgnIndirect(lprc);
}

HOOKFUNC HRGN WINAPI MyCreatePolygonRgn(const POINT *lpPoints, int cPoints, int fnPolyFillMode)
{
	if(gwi.fullScreenGame)
	{
		for(int i = 0; i < cPoints; i++)
		{
			gwi.MapToWindow((LPPOINT)&lpPoints[i]);
		}
	}
	return CreatePolygonRgn(lpPoints, cPoints, fnPolyFillMode);
}

HOOKFUNC BOOL WINAPI MyTextOutA(HDC hdc, int nXStart, int nYStart, LPCSTR lpString, int cchString)
{
	if(gwi.fullScreenGame && GetObjectType(hdc) == OBJ_DC)
	{
		gwi.MapToWindow(&nXStart, &nYStart);
	}
	ThreadLocalStuff& curtls = tls;
	curtls.forceLocale++;
	BOOL rv = TextOutA(hdc, nXStart, nYStart, lpString, cchString);
	curtls.forceLocale--;
	return rv;
}
HOOKFUNC BOOL WINAPI MyTextOutW(HDC hdc, int nXStart, int nYStart, LPWSTR lpString, int cchString)
{
	if(gwi.fullScreenGame && GetObjectType(hdc) == OBJ_DC)
	{
		gwi.MapToWindow(&nXStart, &nYStart);
	}
	ThreadLocalStuff& curtls = tls;
	curtls.forceLocale++;
	BOOL rv = TextOutW(hdc, nXStart, nYStart, lpString, cchString);
	curtls.forceLocale--;
	return rv;
}

HOOKFUNC int WINAPI MyDrawTextA(HDC hdc, LPCSTR lpchText, int nCount, LPRECT lpRect, UINT uFormat)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(lpRect);
	}
	return DrawTextA(hdc, lpchText, nCount, lpRect, uFormat);
}
HOOKFUNC int WINAPI MyDrawTextW(HDC hdc, LPWSTR lpchText, int nCount, LPRECT lpRect, UINT uFormat)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(lpRect);
	}
	return DrawTextW(hdc, lpchText, nCount, lpRect, uFormat);
}

HOOKFUNC int WINAPI MyDrawTextExA(HDC hdc, LPSTR lpchText, int nCount, LPRECT lpRect, UINT dwDTFormat, LPDRAWTEXTPARAMS lpDTParams)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(lpRect);
	}
	return DrawTextExA(hdc, lpchText, nCount, lpRect, dwDTFormat, lpDTParams);
}
HOOKFUNC int WINAPI MyDrawTextExW(HDC hdc, LPWSTR lpchText, int nCount, LPRECT lpRect, UINT dwDTFormat, LPDRAWTEXTPARAMS lpDTParams)
{
	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(lpRect);
	}
	return DrawTextExW(hdc, lpchText, nCount, lpRect, dwDTFormat, lpDTParams);
}

HOOKFUNC COLORREF WINAPI MySetTextColor(HDC hdc, COLORREF crColor)
{
	// TODO: Enable in Compat mode only.
	if(gwi.VirtualPixelFormat.dwRGBBitCount == 8)
	{
		crColor = gwi.GetMatchingColor(crColor);
	}

	return SetTextColor(hdc, crColor);
}

HOOKFUNC COLORREF WINAPI MySetBkColor(HDC hdc, COLORREF crColor)
{
	// TODO: Enable in Compat mode only.
	if (gwi.VirtualPixelFormat.dwRGBBitCount == 8)
	{
		crColor = gwi.GetMatchingColor(crColor);
	}

	return SetBkColor(hdc, crColor);
}

//int CALLBACK EnumFontProc(const LOGFONTA* lplf, const TEXTMETRICA* tma, DWORD FontType, LPARAM lParam)
//{
//	debugprintf(__FUNCTION__ " called (charset=%d, lfFaceName=%s).\n", lplf->lfCharSet, lplf->lfFaceName);
//	return 1;
//}
//int CALLBACK EnumFontProcW(const LOGFONTW* lplf, const TEXTMETRICW* tma, DWORD FontType, LPARAM lParam)
//{
//	debugprintf(__FUNCTION__ " called (charset=%d, lfFaceName=%S).\n", lplf->lfCharSet, lplf->lfFaceName);
//	return 1;
//}

static int CALLBACK DoesFontExistWCallback(CONST LOGFONTW * lplf, CONST TEXTMETRICW * tm, DWORD type, LPARAM param)
{
	//debugprintf(__FUNCTION__": %S,%d,%d\n", lplf->lfFaceName, lplf->lfCharSet, type);
	*((DWORD*)param) = 1;
	return 0;
}
bool DoesFontExistW(CONST LOGFONTW *lplf)
{
	if(!*lplf->lfFaceName)
		return false;
	HDC hdc = GetDC(NULL); // "If this value is NULL, GetDC retrieves the DC for the entire screen."
	DWORD rv = 0;
	EnumFontFamiliesExW(hdc, (LPLOGFONTW)lplf, &DoesFontExistWCallback, (LPARAM)&rv, 0);
	ReleaseDC(NULL, hdc);
	return rv != 0;
}

#define DEFAULT_FONT_FOR_LOCALE_1041 "MS Gothic" // English name of the default system font on Windows XP Japanese systems. Maybe there should be an option to use Meiryo instead (the default on Vista).
// TODO: add defaults for other locales. without this, it should still find a readable font, but it might not look like what people are used to.

// workaround for L"string" not working with macros
#define _L2(x) L##x
#define _L(x) _L2(x)

// The CreateFont functions gets mapped into MyCreateFontIndirect, this is rather inconvenient, but some of the tests that needs to be done
// cannot be done on the data when it's in this form due to the WinAPI lacking the required functions. This is only possible due to the values
// that are stored in BYTES in the LOGFONT struct have pre-defined values from Windows.h, which are shared with this function.
// NOTE: Well... There is actually a WinAPI function that can do it (EnumFontFamilies), but it's marked for removal as it's only really there
// to support 16-bit programs, which are on their way to be phased out as the backwards compatibility for 16-bit encoded opcodes is more and
// more rarely included in new CPUs.
// -- Warepire
HOOKFUNC HFONT WINAPI MyCreateFontA(int nHeight, int nWidth, int nEscapement, int nOrientation, int fnWeight,
								DWORD fdwItalic, DWORD fdwUnderline, DWORD fdwStrikeOut, DWORD fdwCharSet,
								DWORD fdwOutputPrecision, DWORD fdwClipPrecision, DWORD fdwQuality,
								DWORD fdwPitchAndFamily, LPCSTR lpszFace)
{
	LOGFONTA lfFont;
	lfFont.lfHeight = nHeight;
	lfFont.lfWidth = nWidth;
	lfFont.lfEscapement = nEscapement;
	lfFont.lfOrientation = nOrientation;
	lfFont.lfWeight = fnWeight;
	lfFont.lfItalic = fdwItalic;
	lfFont.lfUnderline = fdwUnderline;
	lfFont.lfStrikeOut = fdwStrikeOut;
	lfFont.lfCharSet = fdwCharSet;
	lfFont.lfOutPrecision = fdwOutputPrecision;
	lfFont.lfClipPrecision = fdwClipPrecision;
	lfFont.lfQuality = fdwQuality;
	lfFont.lfPitchAndFamily = fdwPitchAndFamily;
	strncpy(lfFont.lfFaceName, lpszFace, 32);

	return MyCreateFontIndirectA(&lfFont);
}
HOOKFUNC HFONT WINAPI MyCreateFontW(int nHeight, int nWidth, int nEscapement, int nOrientation, int fnWeight,
								DWORD fdwItalic, DWORD fdwUnderline, DWORD fdwStrikeOut, DWORD fdwCharSet,
								DWORD fdwOutputPrecision, DWORD fdwClipPrecision, DWORD fdwQuality,
								DWORD fdwPitchAndFamily, LPWSTR lpszFace)
{
	LOGFONTW lfFont;
	lfFont.lfHeight = nHeight;
	lfFont.lfWidth = nWidth;
	lfFont.lfEscapement = nEscapement;
	lfFont.lfOrientation = nOrientation;
	lfFont.lfWeight = fnWeight;
	lfFont.lfItalic = fdwItalic;
	lfFont.lfUnderline = fdwUnderline;
	lfFont.lfStrikeOut = fdwStrikeOut;
	lfFont.lfCharSet = fdwCharSet;
	lfFont.lfOutPrecision = fdwOutputPrecision;
	lfFont.lfClipPrecision = fdwClipPrecision;
	lfFont.lfQuality = fdwQuality;
	lfFont.lfPitchAndFamily = fdwPitchAndFamily;
	wcsncpy(lfFont.lfFaceName, lpszFace, 32);

	return MyCreateFontIndirectW(&lfFont);
}

HOOKFUNC HFONT WINAPI MyCreateFontIndirectA(CONST LOGFONTA *lplf)
{
	debuglog(LCF_GDI, __FUNCTION__ " called (quality=%d, charset=%d, lfFaceName=%s).\n", lplf->lfQuality, lplf->lfCharSet, lplf->lfFaceName);

	if(lplf->lfQuality != NONANTIALIASED_QUALITY)
		((LOGFONTA*)lplf)->lfQuality = ANTIALIASED_QUALITY; // disable ClearType so it doesn't get into AVIs

	HFONT rv;
	if(tasflags.appLocale
	&& (lplf->lfCharSet == LocaleToCharset(tasflags.appLocale)
	 || (/*tasflags.movieVersion >= 79
	  && */(lplf->lfCharSet == DEFAULT_CHARSET || lplf->lfCharSet == OEM_CHARSET)))
		/*&& lplf->lfCharSet != LocaleToCharset(GetACP())*/)
	{
		// since windows 2000, the CreateFont functions can recognize either the localized or unlocalized font name.
		// but, they still can't recognize the localized font name in the incorrect codepage.
		str_to_wstr(wstr, lplf->lfFaceName, LocaleToCodePage(tasflags.appLocale));

		LOGFONTW fontw;
		memcpy(&fontw, lplf, (int)&fontw.lfFaceName - (int)&fontw);
		wcscpy(fontw.lfFaceName, wstr);
		if(/*tasflags.movieVersion >= 79 && */tasflags.appLocale == 1041 && !DoesFontExistW(&fontw))
		{
			wcscpy(fontw.lfFaceName, _L(DEFAULT_FONT_FOR_LOCALE_1041));
			if(!DoesFontExistW(&fontw))
				wcscpy(fontw.lfFaceName, wstr);
		}
		rv = CreateFontIndirectW(&fontw);
	}
	else
	{
		rv = CreateFontIndirectA(lplf);
	}

	return rv;
}
HOOKFUNC HFONT WINAPI MyCreateFontIndirectW(CONST LOGFONTW *lplf)
{
	debuglog(LCF_GDI, __FUNCTION__ " called (quality=%d, charset=%d, lfFaceName=%S).\n", lplf->lfQuality, lplf->lfCharSet, lplf->lfFaceName);
	if(lplf->lfQuality != NONANTIALIASED_QUALITY)
		((LOGFONTW*)lplf)->lfQuality = ANTIALIASED_QUALITY; // disable ClearType so it doesn't get into AVIs

	LOGFONTW fontw;
	if(/*tasflags.movieVersion >= 79 && */tasflags.appLocale
	&& (lplf->lfCharSet == LocaleToCharset(tasflags.appLocale)
	 || lplf->lfCharSet == DEFAULT_CHARSET || lplf->lfCharSet == OEM_CHARSET))
	{
		if(tasflags.appLocale == 1041 && !DoesFontExistW(lplf))
		{
			memcpy(&fontw, lplf, sizeof(LOGFONTW));
			wcscpy(fontw.lfFaceName, _L(DEFAULT_FONT_FOR_LOCALE_1041));
			if(DoesFontExistW(&fontw))
				lplf = &fontw;
		}
	}

	HFONT rv = CreateFontIndirectW(lplf);
	return rv;
}

HOOKFUNC BOOL WINAPI MySetViewportOrgEx(HDC hdc, int X, int Y, LPPOINT lpPoint)
{
	BOOL rv;

	if(gwi.IsVirtual(hdc))
	{
		gwi.VirtualOffsetX = X;
		gwi.VirtualOffsetY = Y;
		return TRUE;
	}

	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(&X, &Y);
	}

	rv = SetViewportOrgEx(hdc, X, Y, lpPoint);
	if(rv && lpPoint)
	{
		// Need to give the game back it's viewport as it thinks it set it.
		if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
		{
			gwi.UnmapFromWindow(lpPoint);
		}
	}
	return rv;
}

HOOKFUNC BOOL WINAPI MySetViewportExtEx(HDC hdc, int nXExtent, int nYExtent, LPSIZE lpSize)
{
	BOOL rv;

	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(&nXExtent, &nYExtent);
	}

	rv = SetViewportExtEx(hdc, nXExtent, nYExtent, lpSize);
	if(rv && lpSize)
	{	
		if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
		{
			gwi.UnmapFromWindow((LPPOINT)lpSize);
		}
	}
	return rv;
}

HOOKFUNC BOOL WINAPI MyGetViewportOrgEx(HDC hdc, LPPOINT lpPoint)
{
	BOOL rv;

	if(gwi.IsVirtual(hdc))
	{
		lpPoint->x = gwi.VirtualOffsetX;
		lpPoint->y = gwi.VirtualOffsetY;
		return TRUE;
	}

	rv = GetViewportOrgEx(hdc, lpPoint);
	if(rv)
	{
		if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
		{
			gwi.UnmapFromWindow(lpPoint);
		}
	}
	return rv;
}

HOOKFUNC BOOL WINAPI MyGetWindowOrgEx(HDC hdc, LPPOINT lpPoint)
{
	BOOL rv;

	rv = GetWindowOrgEx(hdc, lpPoint);
	if(rv)
	{
		if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
		{
			gwi.UnmapFromWindow(lpPoint);
		}
	}
	return rv;
}

HOOKFUNC BOOL WINAPI MySetWindowOrgEx(HDC hdc, int X, int Y, LPPOINT lpPoint)
{
	BOOL rv;

	if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
	{
		gwi.MapToWindow(&X, &Y);
	}

	rv = SetWindowOrgEx(hdc, X, Y, lpPoint);
	if(rv && lpPoint)
	{
		if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
		{
			gwi.UnmapFromWindow(lpPoint);
		}
	}
	return rv;
}

HOOKFUNC BOOL WINAPI MyGetCurrentPositionEx(HDC hdc, LPPOINT lpPoint)
{
	BOOL rv;

	rv = GetCurrentPositionEx(hdc, lpPoint);
	if(rv)
	{
		if(gwi.fullScreenGame && (OBJ_DC == GetObjectType(hdc)))
		{
			gwi.UnmapFromWindow(lpPoint);
		}
	}
	return rv;
}

// TODO: Fold common code into a __forceinline function.
HOOKFUNC LONG WINAPI MyChangeDisplaySettingsA(LPDEVMODEA lpDevMode, DWORD dwFlags)
{
	if(lpDevMode)
		debugprintf(__FUNCTION__ "(width=%d, height=%d, flags=0x%X) called.\n", lpDevMode->dmPelsWidth, lpDevMode->dmPelsHeight, dwFlags);
	else
		debugprintf(__FUNCTION__ "(flags=0x%X) called.\n", dwFlags);	
	//if(/*tasflags.forceWindowed && */lpDevMode /*&& (dwFlags & CDS_FULLSCREEN)*/)
	//{
	//	fakeDisplayWidth = lpDevMode->dmPelsWidth;
	//	fakeDisplayHeight = lpDevMode->dmPelsHeight;
	//	fakePixelFormatBPP = lpDevMode->dmBitsPerPel;
	//	fakeDisplayValid = TRUE;
	//	FakeBroadcastDisplayChange(fakeDisplayWidth,fakeDisplayHeight,fakePixelFormatBPP);
	//	if(gamehwnd)
	//		MakeWindowWindowed(gamehwnd, fakeDisplayWidth, fakeDisplayHeight);
	//
	//	return DISP_CHANGE_SUCCESSFUL;
	//}

	// v2.02.32: reset the emulated DC used in EMULATEDC mode
	gwi.ResetEmulatedDC();

	// save desired settings first v.2.1.89
	// v2.1.95 protect when lpDevMode is null (closing game... Jedi Outcast)
	// v2.2.23 consider new width/height only when dmFields flags are set.
	if(lpDevMode && (lpDevMode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT))){
		RECT client;
		gwi.dwScreenWidth = lpDevMode->dmPelsWidth;
		gwi.dwScreenHeight = lpDevMode->dmPelsHeight;
		gwi.dwPixelFormatBPP = lpDevMode->dmBitsPerPel;
		//dxw.SetScreenSize(lpDevMode->dmPelsWidth, lpDevMode->dmPelsHeight);
		// v2.02.31: when main win is bigger that expected resolution, you're in windowed fullscreen mode
		//(*pGetClientRect)((dxw.dwFlags1 & FIXPARENTWIN) ? dxw.hParentWnd : dxw.GethWnd(), &client);

		GetClientRect(gwi.hWnd, &client); // Just use the maximum allowed resolution instead?

		//OutTraceD("%s: current hWnd=%x size=(%d,%d)\n", fname, dxw.GethWnd(), client.right, client.bottom);
		if((client.right >= (LONG)lpDevMode->dmPelsWidth) && (client.bottom >= (LONG)lpDevMode->dmPelsHeight)) {
			//OutTraceD("%s: entering FULLSCREEN mode\n", fname);
			//dxw.SetFullScreen(TRUE);
			gwi.fullScreenGame = true;
			// TODO: More stuff, or do we only really mark it as a full screen game?
		}
	}

	if ((dwFlags == 0 || dwFlags == CDS_FULLSCREEN) && lpDevMode)
	{
		//if (dxw.dwFlags1 & EMULATESURFACE || !(lpDevMode->dmFields & DM_BITSPERPEL)){
			//OutTraceD("%s: BYPASS res=DISP_CHANGE_SUCCESSFUL\n", fname);
		return DISP_CHANGE_SUCCESSFUL;
	}
	else
	{
		LONG rv = ChangeDisplaySettingsA(lpDevMode, dwFlags);
		if(lpDevMode) FakeBroadcastDisplayChange(lpDevMode->dmPelsWidth,lpDevMode->dmPelsHeight,lpDevMode->dmBitsPerPel);
		return rv;
	}
}

HOOKFUNC LONG WINAPI MyChangeDisplaySettingsW(LPDEVMODEW lpDevMode, DWORD dwFlags)
{
	if(lpDevMode)
		debugprintf(__FUNCTION__ "(width=%d, height=%d, flags=0x%X) called.\n", lpDevMode->dmPelsWidth, lpDevMode->dmPelsHeight, dwFlags);
	else
		debugprintf(__FUNCTION__ "(flags=0x%X) called.\n", dwFlags);
	//if(/*tasflags.forceWindowed && */lpDevMode /*&& (dwFlags & CDS_FULLSCREEN)*/)
	//{
	//	fakeDisplayWidth = lpDevMode->dmPelsWidth;
	//	fakeDisplayHeight = lpDevMode->dmPelsHeight;
	//	fakePixelFormatBPP = lpDevMode->dmBitsPerPel;
	//	fakeDisplayValid = TRUE;
	//	FakeBroadcastDisplayChange(fakeDisplayWidth,fakeDisplayHeight,fakePixelFormatBPP);
	//	if(gamehwnd)
	//		MakeWindowWindowed(gamehwnd, fakeDisplayWidth, fakeDisplayHeight);
	//
	//	return DISP_CHANGE_SUCCESSFUL;
	//}
	
	// v2.02.32: reset the emulated DC used in EMULATEDC mode
	gwi.ResetEmulatedDC();

	// save desired settings first v.2.1.89
	// v2.1.95 protect when lpDevMode is null (closing game... Jedi Outcast)
	// v2.2.23 consider new width/height only when dmFields flags are set.
	if(lpDevMode && (lpDevMode->dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT))){
		RECT client;
		gwi.dwScreenWidth = lpDevMode->dmPelsWidth;
		gwi.dwScreenHeight = lpDevMode->dmPelsHeight;
		gwi.dwPixelFormatBPP = lpDevMode->dmBitsPerPel;
		//dxw.SetScreenSize(lpDevMode->dmPelsWidth, lpDevMode->dmPelsHeight);
		// v2.02.31: when main win is bigger that expected resolution, you're in windowed fullscreen mode
		//(*pGetClientRect)((dxw.dwFlags1 & FIXPARENTWIN) ? dxw.hParentWnd : dxw.GethWnd(), &client);

		GetClientRect(gwi.hWnd, &client); // Just use the maximum allowed resolution instead?

		//OutTraceD("%s: current hWnd=%x size=(%d,%d)\n", fname, dxw.GethWnd(), client.right, client.bottom);
		if((client.right >= (LONG)lpDevMode->dmPelsWidth) && (client.bottom >= (LONG)lpDevMode->dmPelsHeight)) {
			//OutTraceD("%s: entering FULLSCREEN mode\n", fname);
			//dxw.SetFullScreen(TRUE);
			gwi.fullScreenGame = true;
			// TODO: More stuff, or do we only really mark it as a full screen game?
		}
	}

	if ((dwFlags == 0 || dwFlags == CDS_FULLSCREEN) && lpDevMode)
	{
		//if (dxw.dwFlags1 & EMULATESURFACE || !(lpDevMode->dmFields & DM_BITSPERPEL)){
			//OutTraceD("%s: BYPASS res=DISP_CHANGE_SUCCESSFUL\n", fname);
		return DISP_CHANGE_SUCCESSFUL;
	}
	else
	{
		LONG rv = ChangeDisplaySettingsW(lpDevMode, dwFlags);
		if(lpDevMode) FakeBroadcastDisplayChange(lpDevMode->dmPelsWidth,lpDevMode->dmPelsHeight,lpDevMode->dmBitsPerPel);
		return rv;
	}
}

HOOKFUNC LONG WINAPI MyChangeDisplaySettingsExA(LPCSTR lpszDeviceName, LPDEVMODEA lpDevMode, HWND hwnd, DWORD dwFlags, LPVOID lParam)
{
	debugprintf(__FUNCTION__ "called, redirecting...\n");
	// These flags do things not supported by ChangeDisplaySettings, CDS_VIDEOPARAMETERS might actually be important to support...
	if(dwFlags & CDS_VIDEOPARAMETERS || dwFlags & CDS_ENABLE_UNSAFE_MODES || dwFlags & CDS_DISABLE_UNSAFE_MODES)
		return DISP_CHANGE_BADMODE; // DISP_CHANGE_FAILED might be an alternative return value.
	MyChangeDisplaySettingsA(lpDevMode, dwFlags);
}

HOOKFUNC LONG WINAPI MyChangeDisplaySettingsExW(LPWSTR lpszDeviceName, LPDEVMODEW lpDevMode, HWND hwnd, DWORD dwFlags, LPVOID lParam)
{
	debugprintf(__FUNCTION__ "called, redirecting...\n");
	// These flags do things not supported by ChangeDisplaySettings, CDS_VIDEOPARAMETERS might actually be important to support...
	if(dwFlags & CDS_VIDEOPARAMETERS || dwFlags & CDS_ENABLE_UNSAFE_MODES || dwFlags & CDS_DISABLE_UNSAFE_MODES)
		return DISP_CHANGE_BADMODE; // DISP_CHANGE_FAILED might be an alternative return value.
	MyChangeDisplaySettingsW(lpDevMode, dwFlags);
}

// TODO: Perhaps we need to hook more messages in order to dump video?
HOOKFUNC MCIERROR WINAPI MymciSendCommand(MCIDEVICEID IDDevice, UINT uMsg, DWORD_PTR fdwCommand, DWORD_PTR dwParam)
{
	if(gwi.fullScreenGame)
	{
		switch(uMsg)
		{
			case MCI_WINDOW:
			{
				MCI_OVLY_WINDOW_PARMSW *pw = (MCI_OVLY_WINDOW_PARMSW *)dwParam;
				if(gwi.IsDesktop(pw->hWnd))
				{
					pw->hWnd = gwi.hWnd;
				}
				break;
			}
			case MCI_PUT:
			{
				fdwCommand |= MCI_ANIM_RECT; // DxWnd adds this, don't know why.
				break;
			}
		}
	}

	return mciSendCommand(IDDevice, uMsg, fdwCommand, dwParam);
}

void ApplyGDIIntercepts()
{
	static const InterceptDescriptor intercepts [] = 
	{
		// Some functions that have more to do with GDI than anything else are located in user32.
		MAKE_INTERCEPT(1, GDI32, StretchBlt),
		MAKE_INTERCEPT(1, GDI32, BitBlt),
		MAKE_INTERCEPT(1, GDI32, PatBlt),
		MAKE_INTERCEPT(1, GDI32, MaskBlt),
		MAKE_INTERCEPT(1, GDI32, SetDIBitsToDevice),
		MAKE_INTERCEPT(1, GDI32, StretchDIBits),
		MAKE_INTERCEPT(1, GDI32, CreateDCA),
		MAKE_INTERCEPT(1, GDI32, CreateDCW),
		MAKE_INTERCEPT(1, USER32, GetDC),
		MAKE_INTERCEPT(1, USER32, GetDCEx),
		MAKE_INTERCEPT(1, USER32, GetWindowDC),
		MAKE_INTERCEPT(1, USER32, ReleaseDC),
		MAKE_INTERCEPT(1, GDI32, SelectPalette),
		MAKE_INTERCEPT(1, GDI32, RealizePalette),
		MAKE_INTERCEPT(1, GDI32, GetSystemPaletteEntries),
		MAKE_INTERCEPT(1, GDI32, GetPixel),
		MAKE_INTERCEPT(1, GDI32, GetDeviceCaps),
		MAKE_INTERCEPT(1, GDI32, ChoosePixelFormat),
		MAKE_INTERCEPT(/*1*/1, GDI32, SetPixelFormat), // todo? but overriding it breaks tumiki fighters sometimes?
		MAKE_INTERCEPT(1, GDI32, SwapBuffers),

		MAKE_INTERCEPT(1, USER32, BeginPaint),
		MAKE_INTERCEPT(1, USER32, EndPaint),
		MAKE_INTERCEPT(1, USER32, FillRect),
		MAKE_INTERCEPT(1, USER32, FrameRect),
		MAKE_INTERCEPT(1, GDI32, Rectangle),
		MAKE_INTERCEPT(1, GDI32, Polyline),
		MAKE_INTERCEPT(1, GDI32, LineTo),
		MAKE_INTERCEPT(1, GDI32, ArcTo),
		MAKE_INTERCEPT(1, GDI32, MoveToEx),
		MAKE_INTERCEPT(1, GDI32, PolyDraw),
		MAKE_INTERCEPT(1, GDI32, PolylineTo),
		MAKE_INTERCEPT(1, GDI32, PolyBezierTo),
		MAKE_INTERCEPT(1, GDI32, Ellipse),
		MAKE_INTERCEPT(1, GDI32, Polygon),
		MAKE_INTERCEPT(1, GDI32, Arc),
		MAKE_INTERCEPT(1, GDI32, SetPixel),

		MAKE_INTERCEPT(1, GDI32, GetClipBox),
		MAKE_INTERCEPT(1, GDI32, CreateEllipticRgn),
		MAKE_INTERCEPT(1, GDI32, CreateEllipticRgnIndirect),
		MAKE_INTERCEPT(1, GDI32, CreateRectRgn),
		MAKE_INTERCEPT(1, GDI32, CreateRectRgnIndirect),
		MAKE_INTERCEPT(1, GDI32, CreatePolygonRgn),

		MAKE_INTERCEPT(1, GDI32, TextOutA),
		MAKE_INTERCEPT(1, GDI32, TextOutW),
		MAKE_INTERCEPT(1, GDI32, DrawTextA),
		MAKE_INTERCEPT(1, GDI32, DrawTextW),
		MAKE_INTERCEPT(1, GDI32, DrawTextExA),
		MAKE_INTERCEPT(1, GDI32, DrawTextExW),
		MAKE_INTERCEPT(1, GDI32, SetTextColor),
		MAKE_INTERCEPT(1, GDI32, SetBkColor),

		MAKE_INTERCEPT(1, GDI32, CreateFontA),
		MAKE_INTERCEPT(1, GDI32, CreateFontW),
		MAKE_INTERCEPT(1, GDI32, CreateFontIndirectA),
		MAKE_INTERCEPT(1, GDI32, CreateFontIndirectW),

		MAKE_INTERCEPT(1, GDI32, SetViewportOrgEx),
		MAKE_INTERCEPT(1, GDI32, SetViewportExtEx),
		MAKE_INTERCEPT(1, GDI32, GetViewportOrgEx),
		MAKE_INTERCEPT(1, GDI32, GetWindowOrgEx),
		MAKE_INTERCEPT(1, GDI32, SetWindowOrgEx),
		MAKE_INTERCEPT(1, GDI32, GetCurrentPositionEx),
		MAKE_INTERCEPT(1, USER32, ChangeDisplaySettingsA),
		MAKE_INTERCEPT(1, USER32, ChangeDisplaySettingsW),
		MAKE_INTERCEPT(1, USER32, ChangeDisplaySettingsExA),
		MAKE_INTERCEPT(1, USER32, ChangeDisplaySettingsExW),

		// This call does not really belong here, but it's related to drawing.
		MAKE_INTERCEPT(1, WINMM, mciSendCommand),
	};
	ApplyInterceptTable(intercepts, ARRAYSIZE(intercepts));
}

#else
#pragma message(__FILE__": (skipped compilation)")
#endif
